

const { TableRuleTypeEnum } = require('./order.enum');

/**
 * 单据规则计算服务
 */
class BillingCalculatorService {
    /**
     * 根据规则计算费用
     * @param {*} rule 计费规则
     * @param {*} startTime 开始时间
     * @param {*} endTime 结束时间
     */
    static calculatorByRule(rule, startTime, endTime) {
        startTime = new Date(startTime);
        const durationHours = this._calculateDuration(startTime, endTime); // 使用时长（小时）
        switch (rule.rule_type) {
            case TableRuleTypeEnum.FixedFee:    // 时段计费（固定计费）
                return this._calculateFixedFee(rule, startTime);
            case TableRuleTypeEnum.HourlyRate:  // 小时计费
                return this._calculateHourlyRate(rule, durationHours);
            case TableRuleTypeEnum.Segmented:   // 分段计费
                return this._calculateSegmented(rule, durationHours);
            case TableRuleTypeEnum.Package:     // 包场计费
                return this._calculatePackage(rule, durationHours);
            default:
                throw new Error(`未知的计费规则类型: ${rule.rule_type}`);
        }
    }

    /**
     * 计算固定时段费用
     * @param {*} rule 
     * @param {*} startTime 开始时间
     * @returns {number} 费用
     */
    static _calculateFixedFee(rule, startTime) {
        // 检查是否在规则时段内
        if (!this._isInTimePeriod(startTime, rule.start_time, rule.end_time)) {
            throw new Error('使用时间不在固定时段范围内');
        }
        return rule.fixed_fee;
    } 

    /**
     * 计算小时费率
     * @param {*} rule 规则
     * @param {*} durationHours 使用时长
     * @returns {number} 费用
     */
    static _calculateHourlyRate(rule, durationHours) {
        // 取出使用时长，如果小于基础小时，则用基础小时
        const chargeHours = Math.max(
            durationHours,
            rule.base_hours || 1
        );
        const amount = chargeHours * rule.hourly_rate;
        // 如果费用小于低消费用，则用低消费用
        return Math.max(amount, rule.min_consumption || 0);
    }

    /**
     * 计算分段计费
     * @param {*} rule 规则
     * @param {*} durationHours 使用时长（小时） 
     * @returns {number} 费用
     * 
     * [{ "from": 0,"to": 2,"rate": 30 }, {"from":2,"to":6,"rate":25}]
     */
    static _calculateSegmented(rule, durationHours) {
        const segments = JSON.parse(rule.segment_json);
        let remainingHours = durationHours;
        let totalFee = 0;
        for (let i = 0; i < segments.length; i++) {
            // 如果剩余值没有了
            if (remainingHours <= 0) {
                break;
            }
            const segment = segments[i];
            const segStart = segment.from;
            const segEnd = segment.to === null ? Infinity : segment.to;
            const segHours = Math.min(segEnd, remainingHours + segStart) - segEnd; // 区间的结束小时 -
            if (segHours > 0) {
                totalFee += segHours * segment.rate; // 计算总费用：区间小时 * 区间单价
                remainingHours -= segHours; // 减掉当前区间使用的小时，留着下一个区间继续计算；
            }
        }
        // 处理超出最后一段的情况
        if (remainingHours > 0) {
            const lastSegment = segments[segments.length - 1];
            totalFee += remainingHours * lastSegment.rate;
        }
        return totalFee;
    }

    /**
     * 计算包场费用
     * @param {*} rule 规则
     * @param {*} durationHours 使用时长 
     * @returns {number} 费用
     */
    static _calculatePackage(rule, durationHours) {
        if (rule.max_hours && durationHours > rule.max_hours) {
            throw new Error(`包场时间超过最大限制 ${rule.max_hours}小时`);
        }
        return rule.fixed_fee;
    }



    /************************ ----工具方法隔离线---- *****************************/

    /**
     * 计算持续使用时间（小时）
     * @param {*} startTime 
     * @param {*} endTime 
     */
    static _calculateDuration(startTime, endTime) {
        startTime = new Date(startTime).getTime();
        endTime = new Date(endTime).getTime();
        return (endTime - startTime) / (1000 * 60 * 60)
    }

    /**
     * 检查时间是否在时段内
     * @param {*} time 
     * @param {*} startTime 
     * @param {*} endTime 
     * @returns {boolean}
     */
    static _isInTimePeriod(time, startTime, endTime) {
        const timeHours = time.getHours() + time.getMinutes() / 60;
        const start = this._timeStringToHours(startTime);
        const end = this._timeStringToHours(endTime);
        return timeHours >= start && timeHours <= end;
    }

    /**
     * 时间字符串转为小时
     * @param {*} timeStr 时间字符串 12:00:00
     * @return {}
     */
    static _timeStringToHours(timeStr) {
        const [hours, minutes, seconds] = timeStr.split(':').map(Number);
        return hours + minutes / 60 + seconds / 3600;
    }
}

module.exports = { BillingCalculatorService };